Skip to content

C++ 链接报错:Dso missing from command line ​

工具链升级(例如从 GCC 9 换到 GCC 11)之后,代码没有改动,链接期却报出 Dso missing from command line。本文写给碰到这类报错的 C/C++ 开发者:先解释这条报错背后的链接器机制,再给出从正确到临时绕过的几种修法,读完可以独立定位并修复同类问题。

问题现象 ​

GCC 11.4 下编译链接报错:

/lib/aarch64-linux-gnu/libfmt.so.8: error adding symbols: Dso missing from command line

同样的代码和链接方式在 GCC 9.4 下编译正常。

原因 ​

这条报错来自 GNU ld 的既定行为,不是 GCC 11 引入的新策略。两个长期机制叠加在一起:

  • 不解析间接依赖的符号。binutils 2.22(2011 年)起,ld 默认不再从间接依赖的动态库解析符号(--no-copy-dt-needed-entries 成为默认)。代码引用了某个库的符号,而这个库没有显式出现在链接命令里、只作为其他库的间接依赖存在时,ld 会拒绝解析,直接报 Dso missing from command line。
  • 发行版的 --as-needed 默认补丁。上游 GNU ld 的默认值是 --no-as-needed,但 Debian/Ubuntu 长期给自家的 ld 打了 --as-needed 默认补丁:命令行里列出但未被直接引用的库不会写进 DT_NEEDED,进一步压缩了隐式依赖被顺带带上的空间。

在此基础上,ld 对命令行做从左到右的单向扫描、不回溯,所以库不仅要在命令行里,位置也必须正确:-l 选项要放在引用它符号的目标文件之后。

至于「为什么旧工具链能通过、新的不能」,具体触发点随环境而异——链接顺序、依赖库的打包方式、GCC 驱动传给链接器的参数差异都可能参与其中。旧链接能通过,依赖的是宽松时代工具链的偶然行为,并不是受保证的兼容语义;新工具链只是不再容忍这种写法。

解决方案 ​

1. 显式补上缺失的库(推荐) ​

bash
g++ ... main.o other.o -lfmt

-lfmt 必须放在引用它的目标文件之后,顺序反了同样会报错。

2. 用 pkg-config 生成链接参数 ​

bash
g++ ... main.o other.o $(pkg-config --libs fmt)

适合库自带 .pc 文件的场景,避免手写库列表。

3. CMake ​

cmake
find_package(fmt REQUIRED)
target_link_libraries(your_target PRIVATE fmt::fmt)

导入目标由 CMake 负责展开成正确的链接顺序。

4. 间接依赖同样要显式声明 ​

代码没有直接使用 fmt,但链接的某个库依赖它,也属于同一种情形:

bash
g++ ... main.o -lyourlib -lfmt

-lfmt 放在 -lyourlib 之后。

5. 临时绕过(不推荐) ​

bash
g++ ... -Wl,--no-as-needed -lfmt

-Wl,--copy-dt-needed-entries 可以恢复 binutils 2.22 之前「从间接依赖解析符号」的旧行为,同样只应作临时绕过。这两个开关都掩盖了「依赖没有显式声明」这一根本问题,留给后续升级的隐患大于收益。

总结 ​

Dso missing from command line 的根因是隐式依赖:代码用到的库没有以正确的顺序显式出现在链接命令里。这是 ld 多年来的规范行为,新工具链只是不再放行偶然能跑的链接方式。正确做法是把缺失的库补进链接命令(或交给 pkg-config/CMake 生成),而不是用 --no-as-needed、--copy-dt-needed-entries 之类的开关绕过。

参考 ​

最近更新

基于 VitePress 构建